﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
#if XNA
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
#else
using SilverArcade.SilverSprite;
using SilverArcade.SilverSprite.Graphics;
using SilverArcade.SilverSprite.Content;
using SilverArcade.SilverSprite.Input;
#endif

using System.Collections;
using System.IO;
using System.Globalization;

namespace CakeAnimation
{
    public class Bone
    {
        public string Name;

        public Bone Parent;
        public List<Bone> Children;

        public Vector2 LocalPosition;
        public Vector2 Position;

        public float LocalRotation;
        public float Rotation;

        public Vector2 AnchorOffset;
        public Texture2D Texture;
        public float ZDepth;
        public bool Fixed;

        public bool Filtered = false;

        public Bone(string name, Bone parent, Vector2 size, Vector2 offset, float rotation, Texture2D tex, Vector2 anchor)
        {
            Name = name;

            Children = new List<Bone>();
            SetParent(parent);

            LocalPosition = offset;
            LocalRotation = rotation;
            UpdateTransform();

            Texture = tex;
            AnchorOffset = anchor;
        }

        public Bone Clone(Bone parent)
        {
            Bone b = new Bone(Name, parent, Vector2.Zero, LocalPosition, LocalRotation, Texture, AnchorOffset);
            b.ZDepth = ZDepth;

            foreach (Bone child in Children)
            {
                child.Clone(b);
            }

            return b;
        }

        private void SetParent(Bone parent)
        {
            if (Parent != null)
            {
                Parent.Children.Remove(this);
            }

            Parent = parent;

            if (Parent != null)
            {
                Parent.Children.Add(this);
            }
        }

        public void UpdateTransform()
        {
            if (Parent == null)
            {
                Position = LocalPosition;
                Rotation = LocalRotation;
            }
            else
            {
                Position.X = Parent.Position.X + LocalPosition.X * (float)Math.Cos((float)Parent.Rotation) - LocalPosition.Y * (float)Math.Sin((float)Parent.Rotation);
                Position.Y = Parent.Position.Y + LocalPosition.X * (float)Math.Sin((float)Parent.Rotation) + LocalPosition.Y * (float)Math.Cos((float)Parent.Rotation);

                Rotation = LocalRotation + Parent.Rotation;
            }

            // recursively
            foreach (Bone b in Children)
            {
                b.UpdateTransform();
            }
        }

        public Vector2 GetCenter()
        {
            Vector2 center = Position;
            center.X -= AnchorOffset.X * (float)Math.Cos((float)Rotation) - AnchorOffset.Y * (float)Math.Sin((float)Rotation);
            center.Y -= AnchorOffset.X * (float)Math.Sin((float)Rotation) + AnchorOffset.Y * (float)Math.Cos((float)Rotation);
            return center;
        }

        public Vector2 GetJointPosition()
        {
            Vector2 center = Position;
            //center.X -= AnchorOffset.X * (float)Math.Cos((float)Rotation) - AnchorOffset.Y * (float)Math.Sin((float)Rotation);
            //center.Y -= AnchorOffset.X * (float)Math.Sin((float)Rotation) + AnchorOffset.Y * (float)Math.Cos((float)Rotation);
            return center;
        }

        internal Vector2 GetTip()
        {
            Vector2 center = Position;
            center.X -= 2 * (AnchorOffset.X * (float)Math.Cos((float)Rotation) - AnchorOffset.Y * (float)Math.Sin((float)Rotation));
            center.Y -= 2 * (AnchorOffset.X * (float)Math.Sin((float)Rotation) + AnchorOffset.Y * (float)Math.Cos((float)Rotation));
            return center;
        }
    }









    public class Skeleton
    {
        public Bone RootBone;

        public Skeleton(AnimationContext context, string rigFile)
        {
            FileStream file = new FileStream("Content/" + rigFile, FileMode.Open);
            LoadFromStream(context, file);

            file.Close();
        }

        public Skeleton(AnimationContext context, Stream stream)
        {
            LoadFromStream(context, stream);
        }

        public Skeleton()
        {
            
        }

        public Skeleton Clone()
        {
            Skeleton s = new Skeleton();

            s.RootBone = RootBone.Clone(null);

            return s;
        }

        private void LoadFromStream(AnimationContext context, Stream stream)
        {

            TextReader tr = new StreamReader(stream);
            string line;
            while ((line = tr.ReadLine()) != null)
            {
                if (line.Length == 0)
                    continue;

                if (line[0] == '#')
                    continue;


                string[] keyvalue = line.Split('=');
                if (keyvalue.Length != 2)
                    continue;

                string[] tokens = keyvalue[1].Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                if (tokens.Length != 10)
                    continue;

                string name = keyvalue[0];
                Vector2 parentOffset = ParseVector2(tokens, 0);
                Vector2 size = ParseVector2(tokens, 2);
                Vector2 anchorOffset = ParseVector2(tokens, 4);
                string parent = tokens[6];
                string texture = tokens[7];
                float z = float.Parse(tokens[8], CultureInfo.InvariantCulture);
                float rotation = MathHelper.ToRadians(float.Parse(tokens[9], CultureInfo.InvariantCulture));

                Texture2D tex = context.Content.Load<Texture2D>("Textures/" + texture);

                Bone parentBone = parent == "None" ? null : GetBone(parent, RootBone);
                Bone bone = new Bone(
                    name,
                    parentBone,
                    size,
                    parentOffset,
                    rotation,
                    tex,
                    anchorOffset);

                bone.ZDepth = z;

                if (parentBone == null)
                {
                    RootBone = bone;
                    RootBone.Fixed = true;
                }
            }

            tr.Close();
        }

        private Vector2 ParseVector2(string[] tokens, int index)
        {
            Vector2 v;
            v.X = float.Parse(tokens[index], CultureInfo.InvariantCulture);
            v.Y = float.Parse(tokens[index + 1], CultureInfo.InvariantCulture);
            return v;
        }

        public Bone GetClosestBone(Vector2 mousePos)
        {
            float nearestdist = 100;
            Bone nearest = null;
            List<Bone> toProcess = new List<Bone>();
            toProcess.Add(RootBone);
            while (toProcess.Count > 0)
            {
                Bone b = toProcess[toProcess.Count - 1];
                float dist = (b.GetCenter() - mousePos).Length();
                if (dist < nearestdist && !b.Filtered)
                {
                    nearestdist = dist;
                    nearest = b;
                }
                toProcess.RemoveAt(toProcess.Count - 1);

                foreach (Bone child in b.Children)
                {
                    toProcess.Add(child);
                }
            }

            return nearest;
        }

        public Bone GetBone(string name)
        {
            return GetBone(name, RootBone);
        }

        private Bone GetBone(string name, Bone bone)
        {
            if (bone.Name == name)
            {
                return bone;
            }

            foreach (var item in bone.Children)
            {
                Bone b = GetBone(name, item);
                if (b != null)
                {
                    return b;
                }
            }

            return null;
        }

        public IEnumerable<Bone> GetBoneEnumeration()
        {
            List<Bone> toProcess = new List<Bone>();
            toProcess.Add(RootBone);
            while (toProcess.Count > 0)
            {
                Bone b = toProcess[toProcess.Count - 1];
                yield return b;
                toProcess.RemoveAt(toProcess.Count - 1);

                foreach (Bone child in b.Children)
                {
                    toProcess.Add(child);
                }
            }

        }
    }

    public class SkeletonView2
    {
        public float Alpha = 1.0f;
        public float Scale = 1.0f;
        public float ZOffset = 1.0f;
        public Color Tint = Color.White;
        public Vector2 Offset;
        public bool handleControlTints = false;

        public Skeleton MySkeleton { get; set; }

        public void Draw(AnimationContext context)
        {
            DrawRecursively(context, MySkeleton.RootBone);
        }

        private void DrawRecursively(AnimationContext context, Bone bone)
        {
            Draw(context, bone);

            foreach (Bone b in bone.Children)
            {
                DrawRecursively(context, b);
            }
        }

        float t = 0.0f;
        private void Draw(AnimationContext context, Bone bone)
        {
            Color c = Color.White;
            c.A = (Byte)(c.A * Alpha);


            if (Tint != Color.White)
            {
                Byte a = c.A;
                c = Color.Lerp(c, Tint, 0.7f);
                c.A = a;
            }

            t += context.DeltaTime;

            Vector2 pos = bone.GetCenter() * Scale * context.GetWindowScale() + Offset * context.GetWindowScale();

            context.Sb.Draw(
                bone.Texture,
                pos,
                null,
                c,
                bone.Rotation,
                new Vector2(bone.Texture.Width, bone.Texture.Height) * 0.5f,
                Scale * context.GetWindowScale(),
                SpriteEffects.None,
                bone.ZDepth * ZOffset
                );

            /*
            context.Sb.DrawString(
                context.Font,
                "X",
                bone.GetTip() * Scale * context.GetWindowScale() + Offset * context.GetWindowScale(),
                Color.Black);
             //*/
        }

    }
}
